package t2.server.objTraffick;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import t2.server.DoGet64;
import t2.server.DoNC;
import t2.server.DoNI;
import t2.server.RunND;
import t2.server.xbee.XBeeData;
import t2.server.xbee.XBeeNetwork;

import com.google.gson.Gson;
import com.rapplogic.xbee.api.AtCommandResponse;
import com.rapplogic.xbee.api.XBeeAddress64;
import com.rapplogic.xbee.api.XBeeResponse;
import com.rapplogic.xbee.api.zigbee.NodeDiscover;


public class NetworkDataSvr extends HttpServlet {


    private static final long serialVersionUID = 2181048130259638243L;


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        ExecutorService executor = Executors.newFixedThreadPool(1);
        List<XBeeResponse> listND = null;

        /* Use futures to block until value is returned. */
        try {
            /* Before broadcasting ND, get data from the connected coordinator and add it to the Network. */

            /* Container to catch the data. */
            XBeeData xbd = new XBeeData();
            xbd.setDeviceType(NodeDiscover.DeviceType.DEV_TYPE_COORDINATOR.getValue());

            /* Get the number of children associated with the coordinator. */
            Callable<Integer> workerNC = new DoNC();
            Future<Integer> futureNC = executor.submit(workerNC);
            xbd.setNumberOfChildren(futureNC.get());

            /* Find the name of the coordinator. */
            Callable<String> workerNI = new DoNI();
            Future<String> futureNI = executor.submit(workerNI);
            xbd.setNodeId(futureNI.get());

            /* Get the 64-bit address of the coordinator. */
            Callable<XBeeAddress64> workerAddr64 = new DoGet64();
            Future<XBeeAddress64> futureAddr64 = executor.submit(workerAddr64);
            xbd.setAddr(futureAddr64.get());


            /* If this is the first time an ND has been run, add a new entry to the Network. */
            if (XBeeNetwork.hasCoordinator() == false) {
                XBeeNetwork.getNetwork().add(xbd);
                XBeeNetwork.setHasCoordinator(true);
            } else {
                /* This is just a refresh.  Find the coordinator and update it. */
                for (int i = 0; i < XBeeNetwork.getNetwork().size(); i++) {
                    if (XBeeNetwork.getNetwork().get(i).getDeviceType() == NodeDiscover.DeviceType.DEV_TYPE_COORDINATOR
                            .getValue()) {
                        XBeeNetwork.getNetwork().set(i, xbd);
                        break;
                    }
                }
            }


            /* Broadcast an ND command to the network and process the responses from routers & end nodes. */
            Callable<List<XBeeResponse>> workerND = new RunND();
            Future<List<XBeeResponse>> futureND = executor.submit(workerND);
            listND = futureND.get();

            /* Parse all the responses. */
            NodeDiscover nd;
            for (XBeeResponse response : listND) {
                boolean existed = false;
                nd = NodeDiscover.parse((AtCommandResponse) response);

                /* If it's already part of this network, just update the data already in the list. */
                for (int i = 0; i < XBeeNetwork.getNetwork().size(); i++) {
                    if (nd.getNodeAddress64().equals(XBeeNetwork.getNetwork().get(i).getAddr())) {
                        XBeeNetwork.getNetwork().set(
                                i,
                                new XBeeData(nd.getNodeAddress64(), nd.getNodeIdentifier(), nd.getDeviceType()
                                        .getValue()));
                        existed = true;
                        continue;
                    }
                }

                /* It's a new addition.  Add it to the list. */
                if (!existed) {
                    XBeeNetwork.getNetwork().add(
                            new XBeeData(nd.getNodeAddress64(), nd.getNodeIdentifier(), nd.getDeviceType().getValue()));
                }
            }

        }
        catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        finally {
            executor.shutdown();
        }

        Gson gson = new Gson();

        String json = gson.toJson(XBeeNetwork.getNetwork());

        PrintWriter writer = resp.getWriter();
        writer.write(json);
        writer.flush();
    }
}
